/**
 * Copyright &copy; 2021-2026 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
 */
package com.jeeplus.sys.controller;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.jeeplus.aop.logging.annotation.ApiLog;
import com.jeeplus.common.redis.RedisUtils;
import com.jeeplus.common.utils.RequestUtils;
import com.jeeplus.common.utils.ResponseUtil;
import com.jeeplus.config.properties.JeePlusProperties;
import com.jeeplus.core.errors.ErrorConstants;
import com.jeeplus.security.jwt.TokenProvider;
import com.jeeplus.security.util.SecurityUtils;
import com.jeeplus.sys.constant.CacheNames;
import com.jeeplus.sys.constant.CommonConstants;
import com.jeeplus.sys.constant.enums.LogTypeEnum;
import com.jeeplus.sys.model.LoginForm;
import com.jeeplus.sys.service.UserService;
import com.jeeplus.sys.service.dto.UserDTO;
import com.jeeplus.sys.utils.CustomLineCaptcha;
import com.jeeplus.sys.utils.UserUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;
import org.jasig.cas.client.validation.TicketValidationException;
import org.jasig.cas.client.validation.TicketValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Date;
import java.util.UUID;

/**
 * 登录Controller
 *
 * @author jeeplus
 * @version 2021-5-31
 */
@Slf4j
@RestController
@Api(tags = "登录管理")
public class LoginController {

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    private UserService userService;

    @Autowired
    private RedisUtils redisUtils;

    @Value("${cas.service}")
    private String serviceUrl;

    /**
     * 用户登录
     *
     * @param loginForm
     * @return
     */
    @PostMapping("/sys/login")
    @ApiLog(value = "用户登录", type = LogTypeEnum.LOGIN)
    @ApiOperation("登录接口")
    public ResponseEntity login(@RequestBody LoginForm loginForm) {
        ResponseUtil responseUtil = new ResponseUtil ( );
        String username = loginForm.getUsername ( );
        String password = loginForm.getPassword ( );
        String code = loginForm.getCode ( );
        if ( !code.equals ( RedisUtils.getInstance ( ).get ( CacheNames.SYS_CACHE_CODE, loginForm.getUuid ( ) ) ) ) {
            return ResponseEntity.badRequest ( ).body ( ErrorConstants.LOGIN_ERROR_ERROR_VALIDATE_CODE );
        }
        SecurityUtils.login ( username, password, authenticationManager ); //登录操作spring security

        /**
         * 单一登录判断
         */
        if ( !userService.isEnableLogin ( username ) ) {
            throw new DisabledException ( ErrorConstants.LOGIN_ERROR_FORBID_LOGGED_IN_ELSEWHERE );
        }

        //登录成功，生成token
        UserDTO userDTO = UserUtils.getByLoginName ( username );
        String token = TokenProvider.createAccessToken ( username, userDTO.getPassword ( ) );
        responseUtil.add ( TokenProvider.TOKEN, token );
        //更新登录信息
        updateUserLoginInfo ( responseUtil, userDTO, token );

        return responseUtil.ok ( );
    }


    /**
     * cas登录
     * vue 传递ticket参数验证，并返回token
     */
    @ApiLog(value = "单点登录", type = LogTypeEnum.ACCESS)
    @RequestMapping("/sys/casLogin")
    public ResponseEntity casLogin(@RequestParam(name = "ticket") String ticket,
                                    @Value("${cas.server-url-prefix}") String casServer) throws Exception {
       String service = serviceUrl;
        //ticket检验器
        TicketValidator ticketValidator = new Cas20ServiceTicketValidator ( casServer );
        ResponseUtil responseUtil = new ResponseUtil ( );
        try {
            // 去CAS服务端中验证ticket的合法性
            Assertion casAssertion = ticketValidator.validate ( ticket, service );
            // 从CAS服务端中获取相关属性,包括用户名、是否设置RememberMe等
            AttributePrincipal casPrincipal = casAssertion.getPrincipal ( );
            String loginName = casPrincipal.getName ( );
            //System.out.println("用户名"+loginName1);
            //String loginName = "admin";
            // 校验用户名密码
            UserDTO userDTO = UserUtils.getByLoginName ( loginName );
            if ( userDTO != null ) {
                if ( CommonConstants.NO.equals ( userDTO.getLoginFlag ( ) ) ) {
                    throw new LockedException ( ErrorConstants.LOGIN_ERROR_FORBIDDEN );
                }
                // 单点登录实现不需要校验用户名密码
//                SecurityUtils.login (userDTO.getLoginName (), userDTO.getPassword (), authenticationManager  );
                String token = TokenProvider.createAccessToken ( userDTO.getLoginName ( ), userDTO.getPassword ( ) );
                Authentication authentication = TokenProvider.getAuthentication ( token );
                SecurityContextHolder.getContext ( ).setAuthentication ( authentication );
                responseUtil.add ( TokenProvider.TOKEN, token );
                // 更新登录信息
                updateUserLoginInfo ( responseUtil, userDTO, token );

                return responseUtil.ok ( );
            } else {
                AuthenticationException e = new UsernameNotFoundException ( ErrorConstants.LOGIN_ERROR_NOTFOUND );
                log.error ( "用户【loginName:" + loginName + "】不存在!", e );
                throw e;
            }
        } catch (Exception e) {
            log.error ( "Unable to validate ticket [" + ticket + "]", e );
            throw new CredentialsExpiredException ( "未通过验证的ticket [" + ticket + "]", e );
        }

    }

    /**
     * cas登录
     * vue 传递ticket参数验证，并返回token
     */
//    @ApiLog(value = "单点登录", type = LogTypeEnum.ACCESS)
//    @RequestMapping("/sys/casLogin")
//    public ResponseEntity casLogin(HttpServletRequest request) throws Exception {
//       // String service = "http://192.168.0.13:8082/sys/casLogin";
//        HttpSession session = request.getSession(false);
//
//        //ticket检验器
//        //TicketValidator ticketValidator = new Cas20ServiceTicketValidator ( casServer );
//        ResponseUtil responseUtil = new ResponseUtil ( );
//        // 去CAS服务端中验证ticket的合法性
//        //Assertion casAssertion = ticketValidator.validate ( ticket, service );
//        Assertion casAssertion = session != null ? (Assertion)session.getAttribute("_const_cas_assertion_") : null;
//        // 从CAS服务端中获取相关属性,包括用户名、是否设置RememberMe等
//        AttributePrincipal casPrincipal = casAssertion.getPrincipal ( );
//        //String loginName = casPrincipal.getName ( );
//        String loginName = "admin";
//        // 校验用户名密码
//        UserDTO userDTO = UserUtils.getByLoginName ( loginName );
//        if ( userDTO != null ) {
//            if ( CommonConstants.NO.equals ( userDTO.getLoginFlag ( ) ) ) {
//                throw new LockedException ( ErrorConstants.LOGIN_ERROR_FORBIDDEN );
//            }
//            // 单点登录实现不需要校验用户名密码
////                SecurityUtils.login (userDTO.getLoginName (), userDTO.getPassword (), authenticationManager  );
//            String token = TokenProvider.createAccessToken ( userDTO.getLoginName ( ), userDTO.getPassword ( ) );
//            Authentication authentication = TokenProvider.getAuthentication ( token );
//            SecurityContextHolder.getContext ( ).setAuthentication ( authentication );
//            responseUtil.add ( TokenProvider.TOKEN, token );
//            // 更新登录信息
//            updateUserLoginInfo ( responseUtil, userDTO, token );
//
//            return responseUtil.ok ( );
//        } else {
//            AuthenticationException e = new UsernameNotFoundException ( ErrorConstants.LOGIN_ERROR_NOTFOUND );
//            log.error ( "用户【loginName:" + loginName + "】不存在!", e );
//            throw e;
//        }
//
//    }
    /**
     * 退出登录
     *
     * @param request
     * @param response
     * @return
     */
    @ApiOperation("退出登录")
    @ApiLog(value = "退出登录", type = LogTypeEnum.LOGIN)
    @GetMapping("/sys/logout")
    public ResponseEntity logout(HttpServletRequest request, HttpServletResponse response) {
        Authentication auth = SecurityUtils.getAuthentication ( );
        if ( auth != null ) {
            UserUtils.deleteCache ( UserUtils.getCurrentUserDTO ( ) );
            String token = TokenProvider.resolveToken ( request );
            redisUtils.delete ( CacheNames.USER_CACHE_TOKEN, token );
            redisUtils.delete ( CacheNames.USER_CACHE_ONLINE_USERS, token );
            new SecurityContextLogoutHandler ( ).logout ( request, response, auth );
        }
        return ResponseEntity.ok ( "退出成功" );
    }


    /**
     * 获取登陆验证码
     *
     * @throws
     */
    @ApiOperation("获取验证码")
    @ApiLog("获取验证码")
    @GetMapping("/sys/getCode")
    public ResponseEntity getCode() {
        LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha ( 116, 36, 4, 0 );
        String code = lineCaptcha.getCode(); // 生成验证码
        Image captchaImage = lineCaptcha.createImage(code); // 创建图像

        // 生成唯一标识符
        String uuid = UUID.randomUUID().toString();
        // 将验证码放入session
        RedisUtils.getInstance().set(CacheNames.SYS_CACHE_CODE, uuid, code);
        RedisUtils.getInstance().expire(CacheNames.SYS_CACHE_CODE, uuid, 60 * 5);

        // 将图像转换为 Base64，指定格式为 PNG
        String base64Image = ImgUtil.toBase64((BufferedImage) captchaImage, "png");

        // 返回验证码图片和 uuid
        return ResponseUtil.newInstance().add("codeImg", base64Image).add("uuid", uuid).ok();
    }


    /**
     * 更新用户登录信息
     *
     * @param responseUtil
     * @param userDTO
     * @param token
     */
    private void updateUserLoginInfo(ResponseUtil responseUtil, UserDTO userDTO, String token) {

        //更新登录日期
        userDTO.setLoginDate ( new Date ( ) );
        userDTO.setLoginIp ( ServletUtil.getClientIP ( RequestUtils.getRequest ( ) ) );
        userDTO.setToken ( token );

        /**
         * 存储token
         */
        redisUtils.set ( CacheNames.USER_CACHE_TOKEN, token, token );
        redisUtils.expire ( CacheNames.USER_CACHE_TOKEN, token, JeePlusProperties.newInstance ( ).getEXPIRE_TIME ( ) );
        /**
         * 存储在线用户
         */
        redisUtils.set ( CacheNames.USER_CACHE_ONLINE_USERS, token, userDTO );
        redisUtils.expire ( CacheNames.USER_CACHE_ONLINE_USERS, token, JeePlusProperties.newInstance ( ).getEXPIRE_TIME ( ) );

        responseUtil.add ( "oldLoginDate", userDTO.getLoginDate ( ) );
        responseUtil.add ( "oldLoginIp", userDTO.getLoginIp ( ) );

//        userApi.updateUser ( userDTO );
        userService.updateUserLoginInfo ( userDTO );

    }

    public static void main(String[] args) {
        String service = "http://192.168.0.13:8082/sys/casLogin";
        //ticket检验器
        TicketValidator ticketValidator = new Cas20ServiceTicketValidator ( "http://authserver.jlnu.edu.cn/authserver" );
        ResponseUtil responseUtil = new ResponseUtil ( );
        try {
            // 去CAS服务端中验证ticket的合法性
            Assertion casAssertion = ticketValidator.validate("ST-4491123-FNZgH0slWjLAtuv-e2dDuUifulwauthserver2", service);
            System.out.println(casAssertion.getPrincipal().getName());
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
    }
}
